/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  							 abstract_script_expression.cpp

	$Header: /heroes4/abstract_script_expression.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "abstract_script_expression.h"

#include <algorithm>

#include "enum_operations.h"
#include "sized_int_types.h"
#include "streambuf_operators.h"

// --------------------------------------------------------------------------
// Unnamed namespace
// --------------------------------------------------------------------------

namespace
{

	// --------------------------------------------------------------------------
	// t_expression_type_details class template
	// --------------------------------------------------------------------------

	template < typename t_expression >
	struct t_expression_type_details
	{
	public:
		// Types
		typedef typename t_expression::t_type							t_expression_type;
		typedef t_abstract_script_expression_factory< t_expression >	t_factory;
		typedef typename t_expression::t_reconstruct_ptr				t_reconstruct_ptr;

		// Static data
		enum { k_expression_type_count = t_script_expression_type_type_traits< t_expression_type >::k_count };

		// Static member functions
		static t_reconstruct_ptr	reconstruct( std::streambuf& stream, int version );

		static t_reconstruct_ptr	reconstruct_from_map(
										std::streambuf &	stream );
		static t_reconstruct_ptr	reconstruct_from_map(
										std::streambuf &	stream,
										int					format_version );
		static void					register_factory(
										t_expression_type	type,
										t_factory const &	factory );

	private:
		// Types
		typedef t_factory const *	t_factory_ptr_table[ k_expression_type_count ];

		// Static data
		static t_factory_ptr_table	g_factory_ptr_table;

		// Static member functions
		static t_factory const &	get_factory( t_expression_type type );
		static bool					has_missing_entries( t_factory const** table );
	};

	// --------------------------------------------------------------------------
	// t_expression_type_details members
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	template < typename t_expression >
	typename t_expression_type_details< t_expression >::t_factory_ptr_table
	t_expression_type_details< t_expression >::g_factory_ptr_table;

	// check if any entry in the factory is null.  Done this way so that
	// we can actually KNOW which item is missing, as opposed the nearly 
	// useless "std::find" approach.
	template < typename t_expression >
	bool t_expression_type_details< t_expression >::has_missing_entries( t_factory const** table )
	{
		t_expression_type type;

		for (type = t_expression_type(0); type < k_expression_type_count; enum_incr( type ))
		{
			assert( table[type] != 0 );
			if (table[type] == 0)
				return true;
		}
		return false;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	template < typename t_expression >
	inline
	typename t_expression_type_details< t_expression >::t_factory const &
	t_expression_type_details< t_expression >::get_factory(
		t_expression_type	type )
	{
		assert( type >= 0 && type < k_expression_type_count );
		assert( !has_missing_entries( g_factory_ptr_table ) );

		return *g_factory_ptr_table[ type ];
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	template < typename t_expression >
	inline void t_expression_type_details< t_expression >::register_factory(
		t_expression_type	type,
		t_factory const &	factory )
	{
		assert( type >= 0 && type < k_expression_type_count );
		assert( g_factory_ptr_table[ type ] == 0 );

		g_factory_ptr_table[ type ] = &factory;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	template < typename t_expression >
	typename t_expression_type_details< t_expression >::t_reconstruct_ptr
	t_expression_type_details< t_expression >::reconstruct_from_map(
		std::streambuf &	stream )
	{
		int format_version = get< t_uint16 >( stream );
		return reconstruct_from_map( stream, format_version );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	template < typename t_expression >
	typename t_expression_type_details< t_expression >::t_reconstruct_ptr
	t_expression_type_details< t_expression >::reconstruct_from_map(
		std::streambuf &	stream,
		int					format_version )
	{
		std::string type_keyword;
		if ( !read_string16( stream, type_keyword ) )
			return NULL;

		t_expression_type type = t_script_expression_type_type_traits< t_expression_type >::get_type( type_keyword );
		if ( type < 0 || type >= k_expression_type_count )
			return NULL;

		t_reconstruct_ptr result = get_factory( type ).create();

		if ( !result->read_from_map( stream, format_version ) )
			return NULL;

		return result;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	template < typename t_expression >
	typename t_expression_type_details< t_expression >::t_reconstruct_ptr
	t_expression_type_details< t_expression >::reconstruct( std::streambuf& stream, int version )
	{
		std::string type_keyword;
		if ( !read_string16( stream, type_keyword ) )
			return NULL;

		t_expression_type type = t_script_expression_type_type_traits< t_expression_type >::get_type( type_keyword );
		if ( type < 0 || type >= k_expression_type_count )
			return NULL;

		t_reconstruct_ptr result = get_factory( type ).create();

		if ( !result->read( stream, version ) )
			return NULL;

		return result;
	}

} // Unnamed namespace

// --------------------------------------------------------------------------
// t_abstract_script_numeric_expression members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_abstract_script_numeric_expression::t_reconstruct_ptr
t_abstract_script_numeric_expression::reconstruct_from_map(
	std::streambuf &	stream )
{
	return	t_expression_type_details< t_abstract_script_numeric_expression >::reconstruct_from_map(
				stream );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_abstract_script_numeric_expression::t_reconstruct_ptr
t_abstract_script_numeric_expression::reconstruct_from_map(
	std::streambuf &	stream,
	int					format_version )
{
	return	t_expression_type_details< t_abstract_script_numeric_expression >::reconstruct_from_map(
				stream,
				format_version );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_abstract_script_numeric_expression::t_reconstruct_ptr
t_abstract_script_numeric_expression::reconstruct( std::streambuf& stream, int version )
{
	return t_expression_type_details< t_abstract_script_numeric_expression >::reconstruct( stream, version );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_abstract_script_numeric_expression::register_factory(
	t_type				type,
	t_factory const &	factory )
{
	t_expression_type_details< t_abstract_script_numeric_expression >::register_factory(
		type,
		factory );
}

// --------------------------------------------------------------------------
// t_abstract_script_boolean_expression members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_abstract_script_boolean_expression::t_reconstruct_ptr
t_abstract_script_boolean_expression::reconstruct_from_map(
	std::streambuf &	stream )
{
	return	t_expression_type_details< t_abstract_script_boolean_expression >::reconstruct_from_map(
				stream );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_abstract_script_boolean_expression::t_reconstruct_ptr
t_abstract_script_boolean_expression::reconstruct_from_map(
	std::streambuf &	stream,
	int					format_version )
{
	return	t_expression_type_details< t_abstract_script_boolean_expression >::reconstruct_from_map(
				stream,
				format_version );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_abstract_script_boolean_expression::t_reconstruct_ptr
t_abstract_script_boolean_expression::reconstruct( std::streambuf& stream, int version )
{
	return t_expression_type_details< t_abstract_script_boolean_expression >::reconstruct( stream, version );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_abstract_script_boolean_expression::register_factory(
	t_type				type,
	t_factory const &	factory )
{
	t_expression_type_details< t_abstract_script_boolean_expression >::register_factory(
		type,
		factory );
}
